

#### src.gadgets.Auth

from lib import Logger;

class Auth(object):
	NONE = "NONE"
	SIGNED = "SIGNED"
	AUTHENTICATED = "AUTHENTICATED"

	@staticmethod
	def parse(value):
		value = value.strip()

		if (len(value) == 0):
			return Auth.NONE

		value = value.upper()
		
		if (value == Auth.SIGNED):
			return Auth.SIGNED
		elif(value == Auth.AUTHENTICATED):
			return Auth.AUTHENTICATED
		else:
			return Auth.NONE


#### src.gadgets.FeatureSpec

class FeatureSpec(object):
	def __init__(self):
		self.name = "";
		self.params = [];
		self.optional = "";
	
	def getName(self):
		return self.name;
	
	def getParams(self):
		return self.params;
	
	def isOptional(self):
		return self.optional;



#### src.gadget.Gadget
class Gadget(object):
	####### constructor changed, argument order changed
	def __init__(self, context, id = False):
		self.__jsLibraries_ = [];
		self.__substitutions_ = None;
		self.__userPrefValues_ = None;
		self.__oAuthSpec_ = None;
		self.__messageBundle_ = [];
		self.contentTypes = ['HTML', 'URL'];
		self.id = None;
		self.author = None;
		self.authorEmail = None;
		self.description = None;
		self.directoryTitle = None;
		self.contentData = [];
		self.localeSpecs = [];
		self.preloads = [];
		from lib import HashMap;
		self.requires = HashMap();
		self.screenshot = None;
		self.thumbnail = None;
		self.title = None;
		self.titleUrl = null = None;
		self.userPrefs = [];
		self.authorAffiliation = None;
		self.authorLocation = None;
		self.authorPhoto = None;
		self.authorAboutMe = None;
		self.authorQuote = None;
		self.authorLink = None;
		self.showStats = None;
		self.showInDirectory = None;
		self.string = None;
		self.width = None;
		self.height = None;
		self.category = None;
		self.category2 = None;
		self.singleton = None;
		self.renderInline = None;
		self.scaling = None;
		self.scrolling = None;
		from lib import HashMap;
		self.views = HashMap();
		self.links = [];

		if (id):
			self.id = id;
		
		####### temp kludge
		if (context.getUserPrefs()):
			self.setPrefs(context.getUserPrefs());
		
		self.substitutions = Substitutions();

	def setId(self, id):
		self.id = id;
	
	def setPrefs(self, prefs):
		self.__userPrefValues_ = prefs;
	
	def getAuthor(self):
		return self.substitutions.substitute(self.author);
	

	def getAuthorEmail(self):
		return self.substitutions.substitute(self.authorEmail);

	def getMessageBundle(self):
		return self.messageBundle;

	def getDescription(self):
		return self.substitutions.substitute(self.description);
	

	def getDirectoryTitle(self):
		return self.substitutions.substitute(self.directoryTitle);

	def getId(self):
		return self.id;

	def getJsLibraries(self):
		return self.__jsLibraries_;

	def addJsLibrary(self, library):
		self.__jsLibraries_.append(library);

	def getLocaleSpecs(self):
		return self.localeSpecs;

	def getFeatureParams(self, gadget, feature):
		#FIXME not working atm
		spec = gadget.getRequires();
		###### [test]
		if(not spec.has_key(feature.getName())):
			spec = None
		else:
			spec = spec.getValue(feature.getName());

		
		if (spec == None):
			return [self];
		else:
			return spec.getParams();
		
	

	def getPreloads(self):
		return self.preloads;

	def getRequires(self):
		return self.requires;

	def getScreenshot(self):
		return self.substitutions.substitute(self.screenshot);

	def getSubstitutions(self):
		return self.substitutions;

	def getThumbnail(self):
		return self.substitutions.substitute(self.thumbnail);

	def getTitle(self):
		return self.substitutions.substitute(self.title);

	def getTitleUrl(self):
		ret = None;
		if (len(self.titleUrl) != 0):
			ret = self.substitutions.substitute(self.titleUrl);

		return ret;

	def getAuthorAffiliation(self):
		return self.substitutions.substitute(self.authorAffiliation);

	def getAuthorLocation(self):
		return self.substitutions.substitute(self.authorLocation);

	def getAuthorPhoto(self):
		return self.substitutions.substitute(self.authorPhoto);

	def getAuthorAboutme(self):
		return self.substitutions.substitute(self.authorAboutMe);

	def getAuthorQuote(self):
		return self.substitutions.substitute(self.authorQuote);

	def getAuthorLink(self):
		return self.substitutions.substitute(self.authorLink);

	def getShowStats(self):
		return self.showStats;

	def getShowInDirectory(self):
		return self.showInDirectory;

	def getString(self):
		return self.substitutions.substitute(self.string);

	def getWidth(self):
		return self.width;

	def getHeight(self):
		return self.height;

	def getCategory(self):
		return self.substitutions.substitute(self.category);

	def getCategory2(self):
		return self.substitutions.substitute(self.category2);

	def getSingleton(self):
		return self.singleton;

	def getRenderInline(self):
		return self.renderInline;

	def getScaling(self):
		return self.scaling;

	def getScrolling(self):
		return self.scrolling;

	def getUserPrefs(self):
		return self.userPrefs;

	def getUserPrefValues(self):
		return self.__userPrefValues_;

	def setMessageBundle(self, messageBundle):
		self.messageBundle = messageBundle;

	def getLinks(self):
		return self.links;

	def getLink(self, rel):
		for k, link in enumerate(self.links):
			if (link.getRel(self) == rel):
				return link;

		return False;

	def getViews(self):
		return self.views;

	def getView(self, viewName):
		if (self.views.has_key(viewName)):
			return self.views.get(viewName);
		elif (self.views.has_key(GadgetContext.DEFAULT_VIEW)):
			return self.views.get(GadgetContext.DEFAULT_VIEW);
		
		from src.gadgets.servlet import GadgetException;
		raise GadgetException, "Invalid view specified for this gadget";
	

	def getOAuthSpec(self):
		return self.oAuthSpec;

	def setOAuthSpec(self, oAuthSpec):
		self.oAuthSpec = oAuthSpec;


class Substitutions(object):
	

	def __init__(self):
		self.types = {'MESSAGE' : 'MSG', 'BIDI' : 'BIDI', 'USER_PREF' : 'UP', 'MODULE' : 'MODULE'};
	
		self.substitutions = {};

		for (k, type) in enumerate(self.types):
			self.substitutions[type] = {};
		
	

	def addSubstitution(self, typ, key, value):
		if (not (typ in self.substitutions)):
			self.substitutions[typ] = {};
			
		self.substitutions[typ]["__" + typ + "_" + str(key) + "__"] = value;

	def addSubstitutions(self, type, array):
		for key in array:
			self.addSubstitution(type, key, array[key]);


	def substitute(self, inputs):
		for type in self.types:
			inputs = self.substituteType(self.types[type], inputs);
		return inputs;

	def substituteType(self, type, inputs):
		if (type not in self.substitutions or (type in self.substitutions and len(self.substitutions[type]) == 0)):
			return inputs;
		
		#### temp [test]
		# str_replace(search,replace,subject)
		#return str_replace(array_keys(self.substitutions[$type]), array_values(self.substitutions[$type]), $input);
		strtypes = self.substitutions[type];
		import re;
		for key in strtypes:
			
			#[TODO]
			#inputs = string.replace(inputs, holder, value)
			pattern = re.compile(key);
			if(strtypes[key] == None):
				strtypes[key] = '0';
			inputs = pattern.sub(str(strtypes[key]), inputs);			
		return inputs;
		

	
	def substituteUri(self, type, uri):
		if (len(uri) == 0):
			return None;

		try:
			if (len(type) > 0):
				return self.substituteType(type, uri);
			else:
				return self.substitute(uri);

		except:
			return "";



###### src.gagdets.GadgetContext


class GadgetContext():
	DEFAULT_VIEW = 'profile';
	def __init__(self, request, response, renderingContext):
		self.request = request;
		self.response = response;
		self._httpFetcher = None;
		self._locale = None;
		self._renderingContext = None;
		self._registry = None;
		self._userPrefs = None;
		self._gadgetId = None;
		self._view = None;
		self._moduleId = None;
		self._url = None;
		self._cache = None;
		self._blacklist = None;
		self._ignoreCache = None;
		self._forcedJsLibs = None;
		self._containerConfig = None;
		self._container = None;
		self._refreshInterval = None;
		
		#Rendering context is set by the calling event handler (either GADGET or CONTAINER)
		self.setRenderingContext(renderingContext);
		
		#Request variables
		self.setIgnoreCache(self.__getIgnoreCacheParam());
		self.setForcedJsLibs(self.__getFocedJsLibsParam());
		self.setUrl(self.__getUrlParam());
		self.setModuleId(self.__getModuleIdParam());
		self.setView(self.__getViewParam());
		self.setContainer(self.__getContainerParam());
		self.setRefreshInterval(self.__getRefreshIntervalParam());
		#NOTE All classes are initialized when called (aka lazy loading) because we don't need all of them in every situation
	
	def __getRefreshIntervalParam(self):
		if (self.request.get('refresh') != False):
			return self.request.get('refresh');
		else:
			from lib import Config;
			return Config.get('cache_time');

	def __getContainerParam(self):
		container = 'default';
		
		if (self.request.get('container') != False and len(self.request.get('container')) > 0):
			container = self.request.get('container');
		elif (self.request.get('synd') != False and len(self.request.get('synd')) > 0):
			container = self.request.get('synd');

		return container;

	def __getIgnoreCacheParam(self):
		#Support both the old Orkut style &bpc and new standard style &nocache= params
		return (self.request.get('nocache') != False and self.request.get('nocache') == 1) or (self.request.get('bpc')!= False and self.request.get('bpc') == 1);


	def __getFocedJsLibsParam(self):
		if(self.request.get('libs') != False):
			return self.request.get('synd').strip();
		else: 
			return None;

	def __getUrlParam(self):
		if (self.request.get('url') != False and len(self.request.get('url')) > 0):
			return self.request.get('url');
		
		return None;

	def __getModuleIdParam(self):
		try:
			if(self.request.get('mid') != False and self.request.get('mid') == str(int(self.request.get('mid')))):
				return int(self.request.get('mid'));
		except: 
			None;

	def __getViewParam(self):
		if(self.request.get('view') != None and len(self.request.get('view')) > 0):
			return self.request.get('view');
			
		else:
			return GadgetContext.DEFAULT_VIEW;

	def __instanceBlacklist_(self):
		from lib import Config;
		blackListClass = Config.get('blacklist_class');
		if (blackListClass != None):
			return blackListClass();
		
		return None;

	def __instanceUserPrefs(self):
		from lib import Config, RequestUtil, Util;

		prefs = {};
		userPrefParamPrefix = Config.get('userpref_param_prefix');
		prefixLength = len(userPrefParamPrefix);

		for key in self.request.arguments():
			val = self.request.get(key);
			if (key[0:prefixLength] == userPrefParamPrefix):
				name = key[0:prefixLength];
				prefs[name] = val;

		return UserPrefs(prefs);

	def __instanceGadgetId_(self, url, moduleId):
		return GadgetId(url, moduleId);

	def __instanceHttpFetcher(self):
		from lib import Config;
		remoteContent = Config.get('remote_content');
		return remoteContent();

	def __instanceCache_(self):
		from lib import Config;
		dataCache = Config.get('data_cache');
		return dataCache();

	def __instanceRegistry_(self):
		#Profiling showed 40% of the processing time was spend in the feature registry
		#So by caching this and making it a one time initialization, we almost double the performance
		
		import md5;
		from lib import Config;
		md5Text = md5.new(Config.get('features_path')).hexdigest();
		
		from lib import Config;
		if(Config.get('cache_objects')):
			registry = self.getCache().get(md5Text);
		else:
			registry = None;

		if (not registry):
			registry = GadgetFeatureRegistry(Config.get('features_path'));
			
			if(Config.get('cache_objects')):
				self.getCache().set(md5Text, registry);

		return registry;

	def __instanceLocale_(self):
		#Get language and country params, try the GET params first, if their not set try the POST, else use 'all' as default
		from lib import RequestUtil;
		if (self.request.get('lang')!= False and len(self.request.get('lang')) > 0):
			language = self.request.get('lang');
		else:
			language = 'all';

		if (self.request.get('country') != False and len(self.request.get('country')) > 0):
			country = self.request.get('country');
		else:
			country = 'all';
		
		from src.common import Locale;

		return Locale(language, country);

	def __instanceContainerConfig_(self):
		from lib import Config;
		return ContainerConfig(Config.get('container_path'));

	def getContainer(self):
		return self._container;

	def getContainerConfig(self):
		if (self._containerConfig == None):
			self._containerConfig = self.__instanceContainerConfig_();

		return self._containerConfig;

	def getCache(self):
		if (self._cache == None):
			self.setCache(self.__instanceCache_());
		return self._cache;

	def getGadgetId(self):
		if (self._gadgetId == None):
			self.setGadgetId(self.__instanceGadgetId_(self.getUrl(), self.getModuleId()));
		
		return self._gadgetId;

	def getModuleId(self):
		return self._moduleId;

	def getRegistry(self):
		if (self._registry == None):
			self.setRegistry(self.__instanceRegistry_());

		return self._registry;

	def getUrl(self):
		return self._url;

	def getUserPrefs(self):
		if (self._userPrefs == None):
			self.setUserPrefs(self.__instanceUserPrefs());

		return self._userPrefs;

	def getView(self):
		return self._view;

	def setRefreshInterval(self, interval):
		self._refreshInterval = interval;

	def setContainer(self, container):
		self._container = container;

	def setContainerConfig(self, containerConfig):
		self._containerConfig = containerConfig;

	def setBlacklist(self, blacklist):
		self._blacklist = blacklist;

	def setCache(self, cache):
		self._cache = cache;

	def setGadgetId(self, gadgetId):
		self._gadgetId = gadgetId;

	def setHttpFetcher(self, httpFetcher):
		self._httpFetcher = httpFetcher;

	def setLocale(self, locale):
		self._locale = locale;

	def setModuleId(self, moduleId):
		self._moduleId = moduleId;

	def setRegistry(self, registry):
		self._registry = registry;

	def setRenderingContext(self, renderingContext):
		self._renderingContext = renderingContext;

	def setUrl(self, url):
		self._url = url;

	def setUserPrefs(self, userPrefs):
		self._userPrefs = userPrefs;

	def setView(self, view):
		self._view = view;

	def setIgnoreCache(self, ignoreCache):
		self.ignoreCache = ignoreCache;

	def setForcedJsLibs(self, forcedJsLibs):
		self._forcedJsLibs = forcedJsLibs;

	def getRefreshInterval(self):
		return self._refreshInterval;

	def getIgnoreCache(self):
		return self.ignoreCache;

	def getForcedJsLibs(self):
		return self._forcedJsLibs;

	def getBlacklist(self):
		if (self._blacklist == None):
			self.setBlacklist(self.__instanceBlacklist_());

		return self._blacklist;

	def getRenderingContext(self):
		return self._renderingContext;

	def getHttpFetcher(self):
		if (self._httpFetcher == None):
			self.setHttpFetcher(self.__instanceHttpFetcher());

		return self._httpFetcher;

	def getLocale(self):
		if (self._locale == None):
			self.setLocale(self.__instanceLocale_());

		return self._locale;

	def getFeatureRegistry(self):
		return self.registry;

	#
	#Extracts the 'st' token from the GET or POST params and calls the
	#signer to validate the token
	
	#@param GadgetSigner $signer the signer to use (configured in config.php)
	#@return string the token to use in the signed url
	
	def extractAndValidateToken(self, signer):
		if (signer == None or signer == False):
			return None;
		
		
		token = '';
		
		if(self.request.get('st') != False):
			token = self.request.get('st');

		if (len(token.split(':')) != 6):
			import  urllib;
			token = urllib.quote(token.decode('base64'));
		
		return signer.createToken(token);





class UserPrefs:
	def __init__(self, prefes):
		self.prefs = prefes;

	def getPrefs(self):
		return self.prefs;

	def getPref(self, name):
		if(self.prefs.has_key(name)):
			return self.prefs[name];
		else: 
			return None;


class LocaleSpec:
	def __init__(self):
		self.url = None;
		self.locale = None;
		self.rightToLeft = None;
		self.localeMessageBundles = [];

	def getLocaleMessageBundles(self):
		return self.localeMessageBundles;

	def getURI(self):
		return self.url;

	def getLocale(self):
		return self.locale;

	def isRightToLeft(self):
		return self.rightToLeft;




class GadgetId:
	def __init__(self, uri, moduleId):
		self.__uri_ = uri;
		self.__moduleId_ = moduleId;
	

	def getURI(self):
		return self.__uri_;

	def getModuleId(self):
		return self.__moduleId_;

	def getKey(self):
		return self.getURI();





class LinkSpec:
	
	def __init__(self, rel, href, method = 'GET'):
		self.rel = rel;
		self.href = href;
		self.method = method;

	def getRel(self):
	
		return self.rel;
	

	def getHref(self):
	
		return self.href;
	

	def getMethod(self):
	
		return self.method;
	


class LocalMessageBundle :
	def __init__(self, name, desc):
		self.__name_ = name;
		self.__desc_ = desc;

	def getName(self):
		return self.name;

	def getDesc(self):
		return self.desc;



class UserPref:
	def __init__(self):
		self.DataTypes = ['STRING', 'HIDDEN', 'BOOL', 'ENUM', 'LIST', 'NUMBER'];
		self.dataType = None;
		self.name = None;
		self.displayName = None;
		self.defaultValue = None;
		self.required = None;
		self.enumValues = {};
		self.contentType = None;

	def getName(self):
		return self.name;

	def getDisplayName(self):
		return self.displayName;

	def getDefaultValue(self):
		return self.defaultValue;

	def isRequired(self):
		return self.required;

	def getDataType(self):
		return self.dataType;

	def getEnumValues(self):
		return self.enumValues;



# Represents a Content section, but normalized into an individual
# view value after views are split on commas.

class ViewSpec:

	def __init__(self, name, gadgetContent):
		self.href = None;
		self.content = '';
		self.rewrittenContent = None;
		self.name = name;
		self.preferedHeight = '';
		self.preferedWidth = '';

		arr = ['view', 'quirks',  'prefered_height', 'prefered_width', 'type', 'href'];
		attributes = {};
		
		for ar in arr:
			if(gadgetContent.get(ar) != None):
				attributes[ar] = gadgetContent.get(ar);
		
		if (attributes.has_key('view')):
			self.view = attributes['view'].strip();
		else:
			self.view = '';


		self.quirks = attributes.has_key('quirks');

		if (attributes.has_key('prefered_height')):
			self.preferedHeight = attributes['prefered_height'].strip();

		if (attributes.has_key('prefered_width')):
			self.preferedWidth = attributes['prefered_width'].strip();
		
		if (attributes['type'].strip().lower() == 'url'):
			if (len(attributes['href']) == 0):
				raise SpecParserException, "Malformed <Content> href value";

			self.type = 'URL';
			self.href = attributes['href'].strip();
		else:
			self.type = 'HTML';

	def getName(self):
		return self.name;

	def getType(self):
		return self.type;

	def getHref(self):
		return self.href;

	def getQuirks(self):
		return self.quirks;

	def getContent(self):
		return self.content;

	def getView(self):
		return self.view;

	def addContent(self, data):
		self.content = self.content + data;

	def getRewrittenContent(self):
		return self.rewrittenContent;

	def setRewrittenContent(self, rewrittenContent):
		self.rewrittenContent = rewrittenContent;


class MessageBundle:
	
	def __init__(self, messages = {}):
		self.__messages_ = messages;

	def getMessages(self):
		return self.__messages_;


class MessageBundleParser:

	def __processMessage_(self, messages, msg):
		attr = msg.attrib;
		
		if (attr.has_key('name')):
			messages[attr.get('name').strip()] = msg.text;

	def parse(self, xml):
		from xml.etree import ElementTree
		from lib import XmlBuilder;
		parser = XmlBuilder();

		
		# temp kludge
		# todo
		import re;
		_NON_ASCII = re.compile('[^ -~]')
		xml = _NON_ASCII.sub('', xml);
		# till here

		parser.feed(xml);
		doc = parser.close();

		#doc = ElementTree.XML(xml);
		msgs = doc.findall('msg');

		if (len(msgs) == 0):
			raise Exception, "Invalid XML structure in message bundle";
		
		
		messages = {};
		
		for msg in msgs:
			self.__processMessage_(messages, msg);
		
		
		return MessageBundle(messages);
		


class HttpUtil:
	@staticmethod
	def getView(gadget, context):
		view = gadget.getView(context.getView());

		if (view != None):
			containerConfig = context.getContainerConfig();
			
			config = containerConfig.getConfig('default', 'gadgets.features');

			configViews = config['views'];

			if (configViews.has_key(context.getView())):
				views = configViews[context.getView()];
				if (len(views) > 0):
					aliasesViews = views['aliases'];
					for value in aliasesViews:
						view = gadget.getView(value);
						if (view != None):
							break;
			else:
				view = gadget.getView(GadgetContext.DEFAULT_VIEW);
		return view;



class JsLibrary:

	def __init__(self, type, content, featureName = ''):
		self.__types_ = ['FILE', 'URL', 'INLINE'];
		self.__loaded_ = False;
		#used to track what feature this belongs to
		self.__featureName_ = featureName;
		self.__type_ = type;
		self.__content_ = content;

	def getType(self):
		return self.__type_;

	def getContent(self):
		from lib import Config;
		if (self.__loaded_ == False and self.__type_ == 'FILE'):
			
			
			

			if (Config.get('compress_javascript')):
				
				content = False;

				if (Config.get('cache_objects')):
					dataCache = Config.get('data_cache');
					dataCache = dataCache();
					import md5

					content = dataCache.get(md5.new(self.__content_).hexdigest());

				if (content == False):
					from src.common import JsMin;
					content = JsMin.minify(JsLibrary.__loadData_(self.__content_, self.__type_));

					if (Config.get('cache_objects')):
						dataCache.set(md5.new(self.__content_).hexdigest(), content);

					self.__content_ = content;
				else:
					self.__content_ = content;

			else:
#				from src.common import JsMin;
#				content = JsMin.minify(JsLibrary.__loadData_(self.__content_, self.__type_));
#				dataCache.set(md5.new(self.__content_).hexdigest(), content);
#				self.__content_ = content;
				self.__content_ = JsLibrary.__loadData_(self.__content_, self.__type_);

			self.__loaded_ = True;

		return self.__content_;

	def getFeatureName(self):
		return self.__featureName_;

	def toString(self):
		if (self.__type_ == 'URL'):
			return "<script src=\"" + self.getContent() + "\"></script>";
		else:
			return "\n<script><!--\n" + self.getContent() + "\n--></script>\n";

	@staticmethod
	def create(type, content, name):
		return JsLibrary(type, content, name);

	@staticmethod
	def __loadData_(name, type):
		# we don't really do 'resources', so limiting this to files only
		if (type == 'FILE'):
			return JsLibrary.__loadFile_(name);

		return None;

	@staticmethod
	def __loadFile_(fileName):
		# this hack prevents loadFile from trying to load .jar resources
		if (len(fileName) == 0 or (fileName.find('res://') != -1)):
			return '';

		import os;

		if (not os.path.exists(fileName)):
			raise Exception, "JsLibrary file missing: " + fileName;

		if (os.path.isdir(fileName) == True):
			raise Exception, "JsLibrary file is not a file: " + fileName;
		
		content = '';
		try:
			from lib import Util;
			fp = open(Util.normal_path(fileName), 'r');
			content = fp.read();
			fp.close();
		except:
			raise Exception, "JsLibrary error reading file: " + fileName;
		
		return content;



######### src.gadgets.GadgetServer
class GadgetServer:

	def processGadget(self, context):
		gadget = self.__specLoad_(context);
		self.__featuresLoad_(gadget, context);
		return gadget;

	def __specLoad_(self, context):
		#from lib import Logger;

		if (context.getBlacklist() != None and context.getBlacklist().isBlacklisted(context.getUrl())):
			from src.gadgets.servlet import GadgetException;
			raise GadgetException, "Gadget is blacklisted";
		
		#Logger.quickLog("GadgetServer - __specLoad_ function step 1", Logger.DEBUG);

		from src.common import RemoteContentRequest;

		#Logger.quickLog("GadgetServer - __specLoad_ Fetch URL: " + context.getUrl(), Logger.DEBUG);
		
		request = RemoteContentRequest(context.getUrl());
		xml = context.getHttpFetcher().fetch(request, context);

		#Logger.quickLog("GadgetServer - __specLoad_ function step 2", Logger.DEBUG);

		if (xml.getHttpCode() != 200):
			from src.gadgets.servlet import GadgetException;

			raise GadgetException, "Failed to retrieve gadget content";

		specParser = GadgetSpecParser();
		#Logger.quickLog("GadgetServer - __specLoad_ function step 3", Logger.DEBUG);
		gadget = specParser.parse(xml.getResponseContent(), context);
		#Logger.quickLog("GadgetServer - __specLoad_ function step 4", Logger.DEBUG);
		return gadget;

	def __getBundle_(self, context, localeSpec = None):
		if (localeSpec != None):
			uri = localeSpec.getURI();
			if (uri != None):
				fetcher = context.getHttpFetcher();
				from src.common import RemoteContentRequest;
				response = fetcher.fetch(RemoteContentRequest(uri), context);
				parser = MessageBundleParser();
				bundle = parser.parse(response.getResponseContent());
				return bundle;
			else:
				messages = {};
				for messageBundle in localeSpec.getLocaleMessageBundles():
					messages[messageBundle.getName()] = messageBundle.getDesc();
				
				return MessageBundle(messages);

		return None;

	
	def __localeSpec_(self, gadget, local):
		localeSpecs = gadget.getLocaleSpecs();
		for locSpec in localeSpecs:
			#fix me
			if (locSpec.getLocale().equals(local)):
				return locSpec;

		return None;

	
	

	def __getLocaleSpec_(self, gadget, context):
		local = context.getLocale();
		# en-US
		localeSpec = self.__localeSpec_(gadget, local);
		from src.common import Locale;
		if (localeSpec == None):
			# en-all
			localeSpec = self.__localeSpec_(gadget, Locale(local.getLanguage(), "all"));

		if (localeSpec == None):
			#  all-all
			localeSpec = self.__localeSpec_(gadget, Locale("all", "all"));

		return localeSpec;

	

	def __featuresLoad_(self, gadget, context):
		# NOTE i've been a bit liberal here with folding code into this function, 
		# while it did get a bit long, the many include()'s are slowing us down
		# Should really clean this up a bit in the future though
		#from lib import Logger;
		
		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 1", Logger.DEBUG);

		localeSpec = self.__getLocaleSpec_(gadget, context);

		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 2", Logger.DEBUG);
		
		# get the message bundle for this gadget
		bundle = self.__getBundle_(context, localeSpec);

		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 3", Logger.DEBUG);
		
		# FIXME this is a half-assed solution between following the refactoring and 
		# maintaining some of the old code, fixing this up later
		gadget.setMessageBundle(bundle);

		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 4", Logger.DEBUG);
		
		#  perform substitutions
		substitutor = gadget.getSubstitutions();
		
		#  Module ID
		substitutor.addSubstitution('MODULE', "ID", gadget.getId().getModuleId());

		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 5", Logger.DEBUG);
		
		#  Messages (multi-language)
		if (bundle):
			gadget.getSubstitutions().addSubstitutions('MSG', bundle.getMessages());
		
		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 6", Logger.DEBUG);

		#  Bidi support
		rtl = False;
		if (localeSpec != None):
			rtl = localeSpec.isRightToLeft();
		
		if (rtl):
			orientation = "right";
		else:
			orientation = "left";

		substitutor.addSubstitution('BIDI', "START_EDGE", orientation);
		substitutor.addSubstitution('BIDI', "END_EDGE", orientation);
		substitutor.addSubstitution('BIDI', "DIR", orientation);
		substitutor.addSubstitution('BIDI', "REVERSE_DIR", orientation);

		#Logger.quickLog("GadgetServer - __getLocaleSpec_ function step 6", Logger.DEBUG);
		
		# userPref's
		upValues = gadget.getUserPrefValues();
		for pref in gadget.getUserPrefs():
			name = pref.getName();
			value = upValues.getPref(name);
			if (value == None):
				value = pref.getDefaultValue();

			if (value == None):
				value = "";
			
			substitutor.addSubstitution('UP', name, value);
		
		self.__substitutePreloads_(gadget, substitutor);
		
		# Process required / desired features
		requires = gadget.getRequires();
		#Logger.quickLog("Required feature(s): "  + ', '.join(requires.keys()), Logger.DEBUG);

		needed = [];
		optionalNames = [];
		for key in requires.keys():
			entry = requires.getValue(key);
#			entry = requires[key];
			
			needed.append(key);
			if (entry.isOptional()):
				optionalNames.append(key);
		

		#Logger.quickLog("Needed feature(s): "  + ', '.join(needed), Logger.DEBUG);		

		resultsFound = {};
		resultsMissing = {};
		missingOptional = {};
		missingRequired = {};
		resultsFound = context.getRegistry().getIncludedFeaturesResultFound(needed);
		resultsMissing = context.getRegistry().getIncludedFeaturesResultMissing();
		
		for missingResult in resultsMissing.keys():
			if (missingResult in optionalNames):
				missingOptional[missingResult] = missingResult;
			else:
				missingRequired[missingResult] = missingResult;
		
		
		if (len(missingRequired) > 0):
			#Logger.quickLog("Unsupported feature(s): "  + ''.join(missingRequired.keys()), Logger.WARN);

			from src.gadgets.servlet import GadgetException;			
			raise GadgetException, "Unsupported feature(s): "  + ''.join(missingRequired.keys());
		
		# create features
		from lib import HashMap;
		features = HashMap();
		for entry in resultsFound.keys():
			features.add(entry, context.getRegistry().getEntry(entry).getFeature().create());

		# prepare them
		for key in features.keys():			
			params = gadget.getFeatureParams(gadget, context.getRegistry().getEntry(key));
			features.get(key).prepare(gadget, context, params);
		
		# and process them
		
		for key in features.keys():
			params = gadget.getFeatureParams(gadget, context.getRegistry().getEntry(key));
			features.get(key).process(gadget, context, params);
		

	def __substitutePreloads_(self, gadget, substituter):
		preloads = [];
		for preload in gadget.preloads:
			preloads.append(preload.substitute(substituter));
		
		gadget.preloads = preloads;


class GadgetFeatureFactory:
	def create(self):
		from lib import Util;
		Util.abstract();




class GadgetFeatureRegistry:
	
	def __init__(self, featurePath):
		from lib import HashMap;
		self.__features_ = HashMap();
		self.__core_ = HashMap();
		self.__coreDone_ = False;
		self.registerFeatures(featurePath);		
		self.__resultsFound_ = HashMap();
		self.__resultsMissing_ = HashMap();
		#self.__resultsFound_ = {};
		#self.__resultsMissing_ = {};


	def registerFeatures(self, featurePath):
		if (len(featurePath) == 0 or featurePath == None):
			return;
		
		loader = JsFeatureLoader();
		jsFeatures = loader.loadFeatures(featurePath, self);
		if (self.__coreDone_ != False):
			return;
		

		for entry in jsFeatures:
			#if (strtolower(substr(entry.name, 0, len('core'))) == 'core') {
			#if (entry.name.find('core') == 0)
			if (entry.name[0:len('core')] == 'core'):
				self.__core_.add(entry.name, entry.name);
		#  Make sure non-core features depend on core.
		counter = 0;
		for entry in jsFeatures:
			#if (strtolower(substr(entry.name, 0, strlen('core'))) != 'core') {
			if (entry.name[0:len('core')] != 'core'):
				# temp kludge
				from lib import Util;
				entry = entry.deps.array_merge(self.__core_);
				jsFeatures[counter].deps = entry;
				entry = None;
			counter = counter + 1;
			
		self.__coreDone_ = True;
	

	def register(self, name, deps, feature):
		#  Core entries must come first.
		#$entry = isset(self.features[$name]) ? self.features[$name] : None;
		#if (self.__features_.has_key(name)):
		entry = self.__features_.get(name);
		#else:
		#	entry = None

		if (entry == None):
			entry = GadgetFeatureRegistryEntry(name, deps, feature, self);

			if (self.__coreDone_):
				# temp kludge
				#entry.deps = array_merge(entry.deps, self.__core_);
				entry.deps = entry.deps.array_merge(self.__core_);

			self.__features_.add(name, entry);
			self.__validateFeatureGraph_();

		return entry;

	def __validateFeatureGraph_(self):
		#  TODO: ensure that features form a DAG and that all deps are provided
		pass;

	def getAllFeatures(self):
		return self.__features_;

	def getIncludedFeaturesResultFound(self, needed):
		if (len(needed) == 0):
			#  Shortcut for gadgets that don't have any explicit dependencies.
			self.__resultsFound_ = self.__core_;
			return self.__resultsFound_;
		
		for  featureName in needed:
			if (self.__features_.has_key(featureName)):
				feature = self.__features_.get(featureName);
				self.__addFeatureToResults(feature);
			else:
				#self.__resultsMissing_.add(self.__resultsMissing_.len(), featureName);
				self.__resultsMissing_.add(featureName, featureName);
		return self.__resultsFound_;		

	def getIncludedFeaturesResultMissing(self):
		return self.__resultsMissing_;		
	
	def __addFeatureToResults(self, feature):
		if (feature.name in self.__resultsFound_.keys()):
			return;
		
		
		for dep in feature.deps.keys():
			# TODO: Temporal fix, double check where empty dependencies are being added
			if (len(dep) > 0):
				self.__addFeatureToResults(self.__features_.get(dep));
			
		self.__resultsFound_.add(str(feature.name), feature.name);


	def getEntry(self, name):
		return self.__features_.get(name);


#  poor man's namespacing
class GadgetFeatureRegistryEntry:
	

	def __init__(self, name, deps, feature, registry):
		from lib import HashMap;
		#self.deps = {};
		self.deps = HashMap();
		self.name = name;

		if (deps != None):
			for dep in deps:
				entry = registry.getEntry(dep);
				self.deps.add(entry.name, entry.name);
		
		self.feature = feature;

	def getName(self):
		return self.name;

	def getDependencies(self):
		return self.deps;

	def getFeature(self):
		return self.feature;



class GadgetFeature:

	def prepare(self, gadget, context, params):
		from lib import Util;
		Util.abstract();

	def process(self, gadget, context, params):
		from lib import Util;
		Util.abstract();


class GadgetBlacklist:
	 def isBlacklisted(url):
		from lib import Util;
		Util.abstract();




class JsLibraryFeatureFactory(GadgetFeatureFactory):
	
	def __init__(self, gadgetLibraries, containerLibraries):
		#  since we don't do strict type checking, this is one constructor for both a class, or a array of classes
		self.__JsLibraryFeature_ = JsLibraryFeature(gadgetLibraries, containerLibraries);

	def create(self):
		return self.__JsLibraryFeature_;
	

	def getLibraries(self, context):
		if (context == 'GADGET'):
			return self.__JsLibraryFeature_.gadgetLibraries;
		else : 
			return self.__JsLibraryFeature_.containerLibraries;

class JsLibraryFeature(GadgetFeature):
	
	def __init__(self, gadgetLibraries, containerLibraries):
		self.containerLibraries = [];
		self.gadgetLibraries = [];

		#  we have a single constructor for both a single and multiple libraries, so handle it in code instead

		from lib import Util;
		
		if (gadgetLibraries != None and type(gadgetLibraries).__name__ == 'list'):
			self.gadgetLibraries = Util.array_merge(self.gadgetLibraries, gadgetLibraries);
		elif (gadgetLibraries != None and isinstance(gadgetLibraries, JsLibrary)):
			self.gadgetLibraries.append(gadgetLibraries);
		
		if (containerLibraries != None and type(containerLibraries).__name__ == 'list'):
			self.containerLibraries = Util.array_merge(self.containerLibraries, containerLibraries);
		elif (containerLibraries != None and isinstance(containerLibraries, JsLibrary)):
			self.containerLibraries.append(containerLibraries);
		

	def prepare(self, gadget, context, params):
		pass;

	def process(self, gadget, context, params):
		#libraries = {};
		libraries = [];
		
		if (context.getRenderingContext() == 'GADGET'):
			libraries =  self.gadgetLibraries;
		else: 
			libraries = self.containerLibraries;

		
		for library in libraries:
			gadget.addJsLibrary(library);

class SpecParserException(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)


class GadgetSpecParser:

	def parse(self, xml, context):
		if (len(xml) == 0):
			raise SpecParserException("Empty XML document");
		
		#TODO add libxml_get_errors() functionality so we can have a bit more understandable errors..
		
		from xml.etree import ElementTree 
		from lib import XmlBuilder;
		parser = XmlBuilder();

		
		# temp kludge
		# todo
		#import re;
		#_NON_ASCII = re.compile('[^ -~]')
		#xml = _NON_ASCII.sub('', xml);
		# till here

		parser.feed(xml);
		doc = parser.close();
		
		#doc = ElementTree.XML(xml);

		#if ((doc = simplexml_load_string(xml, 'SimpleXMLElement', LIBXML_NOCDATA)) == False) {
		if (doc == False):
			raise SpecParserException("Invalid XML document");
		
		modulePrefs = doc.findall('ModulePrefs');
		

		if (len(modulePrefs) != 1):
			raise SpecParserException("Missing or duplicated <ModulePrefs>");

		modulePrefs = modulePrefs[0];
		gadget = Gadget(context, context.getGadgetId());
		# process ModulePref attributes

		self.__processModulePrefs_(gadget, modulePrefs, context);
		#if (isset(doc.ModulePrefs.OAuth)) {
		# [TODO]
		if (modulePrefs.findall('OAuth')):
			# process OAuthPref attributes
			self.__processOAuthSpec_(gadget, modulePrefs.get('OAuth'), context);

		# process UserPrefs, if any
		for pref in doc.findall('UserPref'):
			self.__processUserPref_(gadget, pref);

		for content in doc.findall('Content'):
			self.__processContent_(gadget, content);
		
		for feature in modulePrefs.findall('Preload'):
			gadget.preloads.append(Preload(feature));
		
		for require in modulePrefs.findall('Require'):
			self.__processFeature_(gadget, require, True);
		
		for optional in modulePrefs.findall('Optional'):
			self.__processFeature_(gadget, optional, False);
		#TODO Parse icons
		return gadget;
	

	def __processModulePrefs_(self, gadget, ModulePrefs, context):
		if (len(ModulePrefs.get('title')) == 0):
			raise SpecParserException, "Missing or empty \"title\" attribute.";

		prefs = [
			"title", "author", "author_email", "description", "directory_title", "screenshot", "thumbnail", 
			"title_url", "author_affiliation", "author_location", "author_photo", "author_aboutme", "author_quote", 
			"author_link", "show_stats", "show_in_directory", "string", "width", "height", "category", 
			"category2", "singleton", "render_inline", "scaling", "scrolling"
			];

		mapped = {
			"author_email" : "authorEmail", "directory_title" : "directoryTitle", "title_url" : "titleUrl",
			"render_inline" : "renderInline", "show_in_directory" : "showInDirectory", "show_stats" : "showStats",
			"author_link" : "authorLink", "author_quote" : "authorQuote", "author_aboutme" : "authorAboutMe",
			"author_photo" : "authorPhoto", "author_location" : "authorLocation", "author_affiliation" : "authorAffiliation",
			};

		for pref in prefs:
			if(mapped.has_key(pref)):
				gagdetName = mapped[pref];
			else:
				gagdetName = pref;

			gadget.__dict__[gagdetName] = ModulePrefs.get(pref).strip() if ModulePrefs.get(pref) != None else '';	
		
		#links = ModulePrefs

		linkTags = ModulePrefs.findall('Link');

		for link in linkTags:
			gadget.links.append(self.__processLink_(link));

		localeTags = ModulePrefs.findall('Locale');
		
		for locales in localeTags:
			gadget.localeSpecs.append(self.__processLocale_(locales, context));


	def __processLink_(self, link):
		
		rel = link.get('rel').strip() if link.get('rel') else '';
		href = link.get('href').strip() if link.get('href') else '';
		method = link.get('method').strip() if link.get('method') else 'GET';
		link = LinkSpec(rel, href, method);
		return link;

	def __processLocale_(self, locales, context):
		messageAttr = locales.get('messages').strip() if locales.get('messages') else '';
		languageAttr = locales.get('lang').strip() if locales.get('lang') else 'all';
		countryAttr = locales.get('country').strip() if locales.get('country') else 'all';
		rtlAttr = locales.get('language_direction').strip() if locales.get('language_direction') else '';
		
		rightToLeft = rtlAttr == 'rtl';

		localeMessageBundles = [];
		if (messageAttr == ''):
			messageBundle = locales.messagebundle;
			if (len(messageBundle) > 0):
				messageName = messageBundle.msg;
				if (len(messageName) > 0):
					for name in messageName:
						attrs = name.attributes();
						localeMessageBundle = LocalMessageBundle(string(attrs['name']), string(name));
						localeMessageBundles[len(localeMessageBundles)] = localeMessageBundle;

		locales = LocaleSpec();
		locales.rightToLeft = rightToLeft;
		#FIXME java seems to use a baseurl here, probably for the http:// part but i'm not sure yet.
		# Should verify behavior later to see if i got it right
		locales.url = messageAttr;
		locales.localeMessageBundles = localeMessageBundles;
		from src.common import Locale;
		locales.locale = Locale(languageAttr, countryAttr);
		return locales;

	def __processUserPref_(self, gadget, pref):
		attributes = {};
		if (pref.get('name') != None):
			attributes['name'] = pref.get('name');

		if (pref.get('display_name') != None):
			attributes['display_name'] = pref.get('display_name');

		if (pref.get('datatype') != None):
			attributes['datatype'] = pref.get('datatype');

		if (pref.get('default_value') != None):
			attributes['default_value'] = pref.get('default_value');

		preference = UserPref();
		if (len(attributes['name']) == 0):
			raise SpecParserException, "All UserPrefs must have name attributes.";
		
		preference.name = attributes['name'].strip();

		preference.displayName = gadget.getSubstitutions().substitute(attributes['display_name'].strip()) if attributes.has_key('display_name') else '';
		# if its set -and- in our valid 'enum' of types, use it, otherwise assume STRING, to try and emulate java's enum behavior
		preference.dataType = attributes['datatype'].upper() if (attributes.has_key('datatype') and (attributes['datatype'].upper() in preference.DataTypes))  else  'STRING';
		preference.defaultValue = gadget.getSubstitutions().substitute(attributes['default_value'].strip()) if attributes.has_key('default_value') else '';
		#if (isset(pref.EnumValue)) 
		if (pref.findall('EnumValue') != None):
			for enum in pref.findall('EnumValue'):
				attr = {};
				attr['value'] = enum.get('value');
				attr['display_value'] = enum.get('display_value');
				valueText = attr['value'].strip();
				if(attr['display_value'] != None):
					displayText = gadget.getSubstitutions().substitute(attr['display_value'].strip()) if (len(attr['display_value']) > 0) else valueText;
				else:
					displayText = valueText;
				preference.enumValues[valueText] = displayText;

		gadget.userPrefs.append(preference);

	def __processContent_(self, gadget, content):
		attributes = {};

		attributes['type'] = content.get('type');
		if (content.get('view') != None):
			attributes['view'] = content.get('view');

		if (len(attributes['type']) == 0):
			raise SpecParserException, "No content type specified!";
		
		view = attributes['view'].strip() if attributes.has_key('view')  else '';
		views = view.split(',');
		html = str(content.text); # no trim here since empty lines can have structural meaning, so typecast to string instead
		
		for view in views:
			if (len(view) == 0):
				view = GadgetContext.DEFAULT_VIEW;
			
			viewSpec = ViewSpec(view, content);
			if (not gadget.views.has_key(view)):
				viewSpec.content = html;
				gadget.views.add(view, viewSpec);
			else:
				if (gadget.views.get(view).getName() == viewSpec.getName() and viewSpec.getType() != gadget.views.get(view).getType()):
					raise SpecParserException, "You may not mix content " + " types in the same view.";
				
				gadget.views.get(view).addContent(html);

	def __processFeature_(self, gadget, feature, required):
		featureSpec = FeatureSpec();
		attributes = feature.get('feature');

		if (len(attributes) == 0):
			raise SpecParserException("Feature not specified in <" + ("Required" if required else "Optional") + "> tag");
		
		featureSpec.name = attributes.strip();
		featureSpec.optional = not (required);

		for param in feature.findall('Param'):
			attr['name'] = param.get('name');
			if (len(attr['name']) == 0):
				raise SpecParserException("Missing name attribute in <Param>.");
			
			name = attr['name'].strip();
			value = param.strip();
			featureSpec.params[name] = value;
		
		gadget.requires.add(featureSpec.name,featureSpec);
#		gadget.requires[featureSpec.name] = featureSpec;

	def __processOAuthSpec_(self, gadget, OAuthSpec):
		oauthSpec = OAuthSpec(OAuthSpec.Service);
		gadget.setOAuthSpec(oauthSpec);



class ContainerConfig:
	
	def __init__(self, defaultContainer):
		self.default_container = 'default';
		self.container_key = 'gadgets.container';
		self.__config_ = {};

		if (len(defaultContainer) > 0):
			self.__loadContainers_(defaultContainer);

	def __loadContainers_(self, containers):
		import os;
		
		if (not (os.path.isdir(containers))): 
			raise  Exception("Invalid container path");

		for file in os.listdir(containers):
			if file.split('.')[-1] != 'js':
				continue;

			file = containers + "/" + file;
			
			if (os.path.isdir(file)):
				#  support recursive loading of sub directories
				self.__loadContainers_(file);
			else:
				self.__loadFromFile_(file);

	def __loadFromFile_(self, file):
		try:
			fp = open(file, 'r');
			contents = fp.read();
			fp.close();
		except:
			raise  Exception, "Could not read container config: " + file;
			
		#  remove all comments (both /#  # / and #  style) because this confuses the json parser
		#  note: the json parser also crashes on trailing ,'s in records so please don't use them
		import re;
		pattern = re.compile('@/\\# (?:.|[\\n\\r])# ?\\# /@');
		contents = pattern.sub('', contents);
		pattern = re.compile('/[^http:\/\/|^https:\/\/]\/\/.# $/m');
		contents = pattern.sub('', contents);

		#contents = preg_replace('@/\\# (?:.|[\\n\\r])# ?\\# /@', '', contents)
		#contents = preg_replace('/[^http:\/\/|^https:\/\/]\/\/.# $/m', '', contents);

		from lib import Util;

		config = Util.json_decode(contents, True);
		
		if (config == contents):
			raise  Exception("Failed to json_decode the container configuration");
		
		if (config.has_key(self.container_key) and len(config[self.container_key]) > 0):
			pass;
		else:
			raise  Exception, "No gadgets.container value set for current container";
		
		container = config[self.container_key][0];
		

		if (not self.__config_.has_key(container)):
			self.__config_[container] = {};

		for key in config.keys():
			self.__config_[container][key] = config[key];

	def getConfig(self, container, name):
		config = {};

		if (self.__config_.has_key(container) and self.__config_[container].has_key(name)):
			config = self.__config_[container][name];

		

		if (container != self.default_container and self.__config_.has_key(container) and self.__config_[container].has_key(name)):
			config = self.__mergeConfig_(self.__config_[container][name], config);

		
		
		return config;
	

	#  Code sniplet borrowed from: http://nl.php.net/manual/en/function.array-merge-recursive.php#81409
	#  default array merge recursive doesn't overwrite values, but creates multiple elementents for that key,
	#  which is not what we want here, we want array_merge like behavior
	def __mergeConfig_(self, *arrays): #  $array1, $array2, etc
	
		narrays = len(arrays);
		i = 0;
		for arr in arrays:
			if (type(arr).__name__ != 'dict'):
				raise Exception, 'Argument #' + (i + 1) + ' is not an array - trying to merge array with scalar! Returning none!';
				return None;
			
			i = i + 1;

			
		ret = arrays[0];
		for arr in narrays:
			for (key, value) in arr:
				if (string(key) == string(int(key))): 
					#  integer or string as integer key - append
					ret.append(value);
				else:
					if ((type(value).__name == 'list' or type(value).__name == 'dict' ) and ret.has_key(key) ):
						ret[key] = self.mergeConfig(ret[key], value);
					else:
						ret[key] = value;

		return ret;


 # 
 #   Handles generation of signing tokens for various request types.
 #   Implementations are free to define their own signing parameters in any
 #   way that is suitable for their site.
 # 
class GadgetSigner:

	 # 
	 #  Generates a token for the given gadget.
	 #  Implementations should also add their own user-related context data
	 #  to the token.
	 #  
	 #  Or generates a token from an input string. This call must produce a token that
	 #  will validate against a token produced directly from a gadget so that the
	 #  following function will always returns a valid SecurityToken:
	 # 
	 #  <code>
	 #  SecurityToken testToken(Gadget gadget, GadgetSigner signer) 
	 #    SecurityToken token = signer.createToken(gadget);
	 #    return signer.createToken(token.toSerialForm());
	 #  }
	 #  </code>
	 # 
	 #  @param tokenString String representation of the token to be created.
	 #  @return The token representation of the input data.
	 #  @throws GadgetException If tokenString is not a valid token
	 # 
	def createToken(self, gadget):
		from lib import Util;
		Util.abstract();
	


 # 
 #  Factory of gadget specs 
 # 
class GadgetSpecFactory:
	#  Return a gadget spec for a context # 
	def getGadgetSpec(self, context):
		from lib import Util;
		Util.abstract();

	#  Return a gadget spec for a URI 
 
	def getGadgetSpecUri(self, url, ignoreCache):
		from lib import Util;
		Util.abstract();



class JsFeatureLoader:

	def loadFeatures(self, path, registry):
		alreadyRegistered = [];
		registeredFeatures = [];
		from lib import HashMap;
		#installedFeatures = {};
		installedFeatures = HashMap();
		self.__loadFiles_(path, installedFeatures);

		#  This ensures that we register everything in the right order.
		for key in installedFeatures.keys():
			feature = installedFeatures.get(key);
			registeredFeature = self.__register_(registry, feature, alreadyRegistered, installedFeatures, registeredFeatures);
			if (registeredFeature != None):
				registeredFeatures.append(registeredFeature);

		return registeredFeatures;
	

	def __sortFeaturesFiles_(self, feature1, feature2):
		import os;
		feature1 = os.path.basename(feature1.replace('/feature.xml', ''));
		feature2 = os.path.basename(feature2.replace('/feature.xml', ''));
		if (feature1 == feature2):
			return 0;
		
		return -1 if (feature1 < feature2) else 1;

	# [TODO] def __loadFiles_(path, &features)
	def __loadFiles_(self, path, features):
		featuresFile = path + '/features.txt';

		import os;

		if (not os.path.exists(featuresFile)):
			return;

		files = [];

		try:
			
			fp = open(featuresFile, 'r');
			files = fp.read().split("\n");

			fp.close();

		except:
			return;

		if (len(files) == 0):
			return;


		#  custom sort, else core.io seems to bubble up before core, which breaks the dep chain order
		
		files = sorted(files, self.__sortFeaturesFiles_);
		from lib import Util;
		for file in files:
			if (file.find('feature.xml') != -1 and Util.substr(file, 0, 1) != '#' and Util.substr(file, 0, 2) != '//'):
				#[TODO] - symbolic link check has been removed

				tempFile = (path + file.replace('features', '').strip()).replace('//', '/');

				#file = os.path.realpath(tempFile);
				
				feature = self.__processFile_(tempFile);
				
				if (feature == None):
					continue;
				features.add(feature.name, feature);


	def __processFile_(self, file):
		feature = None;

		import os;
		
		if (not os.path.exists(file)):
			return feature;
		
		if (not os.path.isfile(file)):
			return feature;
		

		try:
			fp = open(file, 'r');
			content = fp.read();
			fp.close();

			feature = self.__parse_(content, os.path.dirname(file));

		except:
			pass;			
		
		return feature;
	

	def __register_(self, registry, feature, alreadyRegistered, installedFeatures, registeredFeatures):
		if (feature.name in alreadyRegistered):
			return None;
		
		for dep in feature.deps:

			if ((dep in installedFeatures.keys()) and not (dep in alreadyRegistered)):
				registeredFeature = self.__register_(registry, installedFeatures.get(dep), alreadyRegistered, installedFeatures, registeredFeatures);
				if (registeredFeature != None):
					#  add dependency to list of loaded features
					registeredFeatures.append(registeredFeature);
				
			
			#  Note: if a depency is missing, it is simply not loaded. There is no check for that here
		factory = JsLibraryFeatureFactory(feature.gadgetJs, feature.containerJs);
		alreadyRegistered.append(feature.name);
		return registry.register(feature.name, feature.deps, factory);
	

	def __parse_(self, content, path):

		from xml.etree import ElementTree
		from lib import XmlBuilder;
		parser = XmlBuilder();

		parser.feed(content);
		doc = parser.close();

		feature = ParsedFeature();
		feature.basePath = path;

		if (doc.find('name') == None):
			from src.gadgets.servlet import GadgetException;
			raise GadgetException, 'Invalid name in feature: ' + path;

		feature.name = doc.find('name').text.strip();

		for gadget in doc.findall('gadget'):
			feature = self.__processContext_(feature, gadget, False);
		
		for container in doc.findall('container'):
			feature = self.__processContext_(feature, container, True);
		
		for dependency in doc.findall('dependency'):
			feature.deps.append(dependency.text);

		return feature;
	

	def __processContext_(self, feature, context, isContainer):

		for script in context.findall('script'):
			if (script.get('src') == None):
				#  inline content

				type = 'INLINE';
				content = str(script.text);

			else:
				
				content = script.get('src').strip();

				
				
				if (content[0:len("http://")].lower() == "http://"):
					type = 'URL';
				elif (content[0:len("//")].lower() == "//"):
					type = 'URL';
				else:
					#  as before, we skip over the resource parts since we dont support them
					type = 'FILE';
					content = feature.basePath + '/' + content;

			library = JsLibrary.create(type, content, feature.name);

			if (library != None):
				if (isContainer):
					feature.containerJs.append(library);
				else:
					feature.gadgetJs.append(library);

		
		return feature;


class ParsedFeature:
	def __init__(self):
		self.name = "";
		self.basePath = "";
		self.containerJs = [];
		self.gadgetJs = [];
		self.deps = [];




class JsonRpcGadgetContext(GadgetContext):

	def __init__(self, request, response, jsonContext, url):
		self.request = request;
		self.response = response;
		GadgetContext.__init__('GADGET', self.request, self.response);
		self.url = url;
		self.view = jsonContext.view;
		self.locale = Locale(jsonContext.language, jsonContext.country);
		self.container = jsonContext.container;
	

	def getView(self):
		return self.view;



class JsonRpcHandler:

	def process(self, requests):
		response = [];
		for gadget in requests.gadgets:
			try:
				gadgetUrl = gadget.url;
				gadgetModuleId = gadget.moduleId;
				context = JsonRpcGadgetContext(requests.context, gadgetUrl);
				gadgetServer = GadgetServer();
				gadget = gadgetServer.processGadget(context);
				response.append(self.__makeResponse_(gadget, gadgetModuleId, gadgetUrl, context));

			except:
				response.append({'errors' : [e.getMessage()], 
						'moduleId' : gadgetModuleId, 'url' : gadgetUrl});

				#$response[] = array('errors' => array($e->getMessage()), 
				#		'moduleId' => $gadgetModuleId, 'url' => $gadgetUrl);
			
		
		return response;
	

	def __makeResponse_(self, gadget, gadgetModuleId, gadgetUrl, context):
		response = {};
		#prefs = {};
		from lib import HashMap;
		prefs = HashMap();

		for pref in gadget.getUserPrefs():
			prefs.add(pref.getName(), {'displayName' : pref.getDisplayName(), 
					'type' : pref.getDataType(), 'default' : pref.getDefaultValue(), 
					'enumValues' : pref.getEnumValues()});
		
		features = [];
		for feature in gadget.getRequires():
			features.append(feature.getName());
		
		#views = {};		
		views = HashMap();
		for view in gadget.getViews().values():
			# we want to include all information, except for the content
			view.content = None;
			views.add(view.getName(), view);
		
		links = [];
		for link in gadget.links:
			links.append(link);
		
		response['author'] = gadget.getAuthor();
		response['authorEmail'] = gadget.getAuthorEmail();
		response['description'] = gadget.getDescription();
		response['directoryTitle'] = gadget.getDirectoryTitle();
		response['features'] = features;
		response['screenshot'] = gadget.getScreenShot();
		response['thumbnail'] = gadget.getThumbnail();
		response['title'] = gadget.getTitle();
		response['titleUrl'] = gadget.getTitleUrl();
		response['authorAffiliation'] = gadget.getAuthorAffiliation();
		response['authorLocation'] = gadget.getAuthorLocation();
		response['authorPhoto'] = gadget.getAuthorPhoto();
		response['authorAboutme'] = gadget.getAuthorAboutme();
		response['authorQuote'] = gadget.getAuthorQuote();
		response['authorLink'] = gadget.getAuthorLink();
		response['showInDirectory'] = gadget.getShowInDirectory();
		response['showStats'] = gadget.getShowStats();
		response['width'] = gadget.getWidth();
		response['height'] = gadget.getHeight();
		response['categories'] = Array(gadget.getCategory(), gadget.getCategory2());
		response['singleton'] = gadget.getSingleton();
		response['scaling'] = gadget.getScaling();
		response['scrolling'] = gadget.getScrolling();
		response['links'] = links;
		response['views'] = views;
		response['moduleId'] = gadgetModuleId;
		response['url'] = gadgetUrl;
		response['iframeUrl'] = UrlGenerator.getIframeURL(gadget, context);
		response['userPrefs'] = prefs;
		return response;


class ProxyGadgetContext(GadgetContext):

	def __init__(self, request, response, url):
		parent.__init__(request, response, 'GADGET');
		self.url = url;



class Preload:
	AUTHZ_ATTR = "authz";
	
	# 
	 # Creates a Preload from an xml node.
	 # 
	 # @param preload The Preload to create
	 # 
	def __init__(self, preload):
		attributes = preload.attributes();

		self.__signOwner_ = attributes['sign_owner'].strip() if attributes.has_key('sign_owner') else True;
		self.__signViewer_ = attributes['sign_viewer'].strip() if attributes.has_key('sign_viewer') else True;
		self.__href_ = attributes['href'].strip() if attributes.has_key('href') else '';
		
		if (len(self.__href_) == 0):
			raise SpecParserException, "Preload/@href is missing or invalid.";
		
		#  Record all the associated views
		viewNames = attributes['views'].strip() if attributes.has_key('views') else '';
		views = [];
		arrViewNames = viewNames.split(",");
		for view in arrViewNames:
			view = view.strip();
			if (len(view) > 0):
				views.append(view);
			
		
		self.__views_ = views;
		self.__auth_ = Auth.parse(attributes[Preload.AUTHZ_ATTR]);

	def getHref(self):
		return self.__href_;

	def getAuth(self):
		return self.__auth_;

	def isSignViewer(self):
		return self.__signViewer_;

	def isSignOwner(self):
		return self.__signOwner_;

	def getViews(self):
		return self.__views_;

	def substitute(self, substituter):
		return self.__fillPreload_(self, substituter);

	def __fillPreload_(self, preload, substituter):
		self.__signOwner_ = preload.signOwner;
		self.__signViewer_ = preload.signViewer;
		self.__views_ = preload.views;
		self.__auth_ = preload.auth;
		self.__href_ = substituter.substituteUri(None, preload.href);
		return self;
	



# Produces Signing content fetchers for input tokens.
# 
class SigningFetcherFactory:
	 # Produces a signing fetcher that will sign requests and delegate actual
	 # network retrieval to the @code networkFetcher}
	 # 
	 # @param networkFetcher The fetcher that will be doing actual work.
	 # @param token The gadget token used for extracting signing parameters.
	 # @return The signing fetcher.
	 # @throws GadgetException
	 # 
	def getSigningFetcher(self, networkFetcher, token):
		return SigningFetcher.makeFromB64PrivateKey(networkFetcher, token, self.__keyName_, self.__privateKey_);



	 # @param keyFile The file containing your private key for signing requests.
	 # 
	def __init__(self, keyFile = None):
		from lib import Config;
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();

		self.__keyName_ = 'http://' + requestUtilHandler.SERVER["HTTP_HOST"] + Config.get('web_prefix') + '/public.cer';

		if (len(keyFile) > 0):
			privateKey = None;
			try:
				import os;
				if (os.path.exists(keyFile)):
					try:
						fp = open(keyFile);
						rsa_private_key = fp.read();
						fp.close();
					except:
						raise Exception,"Could not read keyfile (" + keyFile + "), check the file name and permission";
					
				
				if (rsa_private_key == None):
					rsa_private_key = '';
				else:
					phrase = Config.get('private_key_phrase') if Config.get('private_key_phrase') != '' else None;
					if (rsa_private_key.find("-----BEGIN") == -1):
						privateKey = privateKey + "-----BEGIN PRIVATE KEY-----\n";
						chunks = str_split(rsa_private_key, 64);
						for chunk in chunks:
							privateKey = privateKey + chunk + "\n";
						
						privateKey = privateKey + "-----END PRIVATE KEY-----";
					else:
						privateKey = rsa_private_key;
					
					rsa_private_key = openssl_pkey_get_private(privateKey, phrase);
					if (not rsa_private_key):
						raise Exception, "Could not create the key";
					
				
			except Exception (e):
				raise Exception, "Error loading private key: " + e.value;
			
			self.__privateKey_ = rsa_private_key;
		





 # Implements signed fetch based on the OAuth request signing algorithm.
 # Subclasses can override signMessage to use their own crypto if they don't
 # like the oauth.net code for some reason.
 # Instances of this class are only accessed by a single thread at a time,
 # but instances may be created by multiple threads.
 # 

from src.common import RemoteContentFetcher;
class SigningFetcher (RemoteContentFetcher):
	
	_OPENSOCIAL_OWNERID = "opensocial_owner_id";
	_OPENSOCIAL_VIEWERID = "opensocial_viewer_id";
	_OPENSOCIAL_APPID = "opensocial_app_id";
	_XOAUTH_PUBLIC_KEY = "xoauth_signature_publickey";
	_ALLOWED_PARAM_NAME = '^[-_[:alnum:]]+';
	
	def __init__(self, next, authToken, keyName, privateKeyObject):
		parent.setNextFetcher(next);
		self._authToken = authToken;
		self._keyName = keyName;
		self._privateKeyObject = privateKeyObject;

	 # 
	 # Authentication token for the user and gadget making the request.
	 # 
	 #self._authToken;
	
	 # Private key we pass to the OAuth RSA_SHA1 algorithm.This can be a
	 # PrivateKey object, or a PEM formatted private key, or a DER encoded byte
	 # array for the private key.(No, really, they accept any of them.)
	 # 
	 #self._privateKeyObject;
	
	 # The name of the key, included in the fetch to help with key rotation.
	 # 
	 #self._keyName;

	 # Constructor based on signing with the given PrivateKey object.
	 # 
	 # @param authToken verified gadget security token
	 # @param keyName name of the key to include in the request
	 # @param privateKey the key to use for the signing
	 # 
	@staticmethod
	def makeFromPrivateKey(next, authToken, keyName, privateKey):
		return SigningFetcher(next, authToken, keyName, privateKey);

	 # Constructor based on signing with the given PrivateKey object.
	 # 
	 # @param authToken verified gadget security token
	 # @param keyName name of the key to include in the request
	 # @param privateKey base64 encoded private key
	 # 
	@staticmethod
	def makeFromB64PrivateKey(next, authToken, keyName, privateKey):
		return SigningFetcher(next, authToken, keyName, privateKey);

	 # Constructor based on signing with the given PrivateKey object.
	 # 
	 # @param authToken verified gadget security token
	 # @param keyName name of the key to include in the request
	 # @param privateKey DER encoded private key
	 # 
	@staticmethod
	def makeFromPrivateKeyBytes(next, authToken, keyName, privateKey):
		return SigningFetcher(next, authToken, keyName, privateKey);
	

	def fetchRequest(self, request):
		return self.getNextFetcher().fetchRequest(request);

	def fetch(self, url, method):
		signed = self.signRequest(url, method);
		return self.getNextFetcher().fetchRequest(signed);
	

	def __signRequest_(self, url, method):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();
		try:
			#  Parse the request into parameters for OAuth signing, stripping out
			#  any OAuth or OpenSocial parameters injected by the client
			parsedUri = parse_url(url);
			resource = url;
			

			queryParams = self.sanitize(requestUtilHandler.GETORPOST);
			postParams = self.sanitize(requestUtilHandler.GETORPOST);
			#  The data that is supposed to be posted to the target page is contained in the postData field
			#  in the _POST to the Shindig proxy server
			#  Here we parse it and put it into the postDataParams array which then is merged into the postParams
			#  to be used for the GET/POST request and the building of the signature
			postDataParams = {};
			if (requestUtilHandler.GETORPOST.has_key('postData')):
				import  urllib;
				postDataParts = urllib.unquote(requestUtilHandler.GETORPOST['postData']).split('&');
				if (len(postDataParts) > 0):
					for postDataPart in postDataParts:
						postDataPartsPair = postDataPart.split('=');
						if (len(postDataPartsPair) == 2):
							postDataParams[postDataPartsPair[0]] = postDataPartsPair[1];
			
			from lib import Util;

			postParams = Util.array_merge(postParams, self.sanitize(postDataParams));
			msgParams = {};
			msgParams = Util.array_merge(msgParams, queryParams);
			msgParams = Util.array_merge(msgParams, postParams);
			self.__addOpenSocialParams_(msgParams);
			self.__addOAuthParams_(msgParams);
			consumer = OAuthConsumer(None, None, None);
			consumer.setProperty(OAuthSignatureMethod_RSA_SHA1.PRIVATE_KEY, self._privateKeyObject);
			signatureMethod = OAuthSignatureMethod_RSA_SHA1();
			req_req = OAuthRequest.from_consumer_and_token(consumer, None, method, resource, msgParams);
			req_req.sign_request(signatureMethod, consumer, None);
			#  Rebuild the query string, including all of the parameters we added.
			#  We have to be careful not to copy POST parameters into the query.
			#  If post and query parameters share a name, they end up being removed
			#  from the query.
			forPost = {};
			postData = False;
			if (method == 'POST'):
				for key in postParams.keys():
					param = postParams[key];
					forPost[key] = param;
					if (postData == False):
						postData = {};
					
					postData.append(OAuthUtil.urlencodeRFC3986(key) + "=" + OAuthUtil.urlencodeRFC3986(param));
				
				if (postData != False):
					postData = postData.join("&");
				
			
			newQuery = '';
			getParams = req_req.get_parameters();
			for key in getParams.keys():
				param = getParams[key];
				if (not forPost.has_key(key)):
					newQuery = newQuery + urllib.quote(key) + '=' + urllib.quote(param) + '&';
				
			
			#  and stick on the original query params too
			if (parsedUri.has_key('query') and len(parsedUri['query']) > 0):
				oldQuery = {};
				parse_str(parsedUri['query'], oldQuery);
				for key in oldQuery.keys():
					val = oldQuery[key];
					newQuery = newQuery + urllib.quote(key) + '=' + urllib.quote(val) + '&';
				
			
			#  Careful here; the OAuth form encoding scheme is slightly different than
			#  the normal form encoding scheme, so we have to use the OAuth library
			#  formEncode method.
			url = parsedUri['scheme'] + '://' + parsedUri['host'] + parsedUri['path'] + '?' + newQuery;
			#  The headers are transmitted in the POST-data array in the field 'Util.headers'
			#  if no post should be made, the value should be False for this parameter
			postheaders = requestUtilHandler.GETORPOST['headers'] if (requestUtilHandler.GETORPOST.has_key('headers') and method == 'POST') else False;
			return RemoteContentRequest(url, postheaders, postData);
		except Exception(e):
			from src.gadgets.servlet import GadgetException;
			raise GadgetException, e.value;
		
	

	def __addOpenSocialParams_(self, msgParams):
		owner = self._authToken.getOwnerId();
		if (owner != None):
			msgParams[SigningFetcher.OPENSOCIAL_OWNERID] = owner;
		
		viewer = self._authToken.getViewerId();
		if (viewer != None):
			msgParams[SigningFetcher.OPENSOCIAL_VIEWERID] = viewer;
		
		app = self._authToken.getAppId();
		if (app != None):
			msgParams[SigningFetcher.OPENSOCIAL_APPID] = app;
		
	

	def __addOAuthParams_(self, msgParams):
		msgParams[OAuth.OAUTH_TOKEN] = '';
		domain = self._authToken.getDomain();
		if (domain != None):
			msgParams[OAuth.OAUTH_CONSUMER_KEY] = domain;
		
		if (self._keyName != None):
			msgParams[SigningFetcher.XOAUTH_PUBLIC_KEY] = self._keyName;
		
		nonce = OAuthRequest.generate_nonce();
		msgParams[OAuth.OAUTH_NONCE] = nonce;
		timestamp = time();
		msgParams[OAuth.OAUTH_TIMESTAMP] = timestamp;
		msgParams[OAuth.OAUTH_SIGNATURE_METHOD] = OAuth.RSA_SHA1;
	

	# 
	 # Strip out any owner or viewer id passed by the client.
	 # 
	def __sanitize_(self, params):
		list = {};
		for key in params.keys():
			p = params[key];
			if (self.allowParam(key)):
				list[key] = p;
			
		
		return list;
	

	def __allowParam_(self, paramName):
		canonParamName = paramName.lower();
		#  Exclude the fields which are only used to tell the proxy what to do
		#  and the fields which should be added by signing the request later on

		arr = ["output", "httpmethod", "authz", "st", "headers", "url", "contenttype", "postdata", "numentries", "getsummaries", "signowner", "signviewer", "gadget", "bypassspeccache"];

		if (canonParamName  in arr):
			return False;

		if(Util.substr(canonParamName, 0, 5) == "oauth" or Util.substr(canonParamName, 0, 6) == "xoauth" or Util.substr(canonParamName, 0, 9) == "opensocial"):
			return False;
	
		#  make a last sanity check on the key of the data by using a regular expression
		return (canonParamName.find(SigningFetcher.ALLOWED_PARAM_NAME) != -1);
	


#  according to features/core/io.js, this is high on the list of things to scrap

UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";

# 
 # The ProxyHandler class does the actual proxy'ing work. it deals both with
 # GET and POST based input, and peforms a request based on the input, Util.headers and 
 # httpmethod params. It also deals with request signing and verification thru the
 # authz and st (security token) params. 
 # 
 # 
class ProxyHandler:
	
	def __init__(self, request, response, context, signingFetcher = None, oauthFetcher = None):
		self.__context_ = context;
		self.__signingFetcher_ = signingFetcher;
		self.__oauthFetcher_ = oauthFetcher;
		self.request = request;
		self.response = response;
	
	 # Fetches content and returns it in JSON format
	 # 
	 # @param string url the url to fetch
	 # @param GadgetSigner signer the request signer to use
	 # @param string method the http method to use (get or post) in making the request
	 # 
	def fetchJson(self, url, signer, method):
		try:
			token = self.__context_.extractAndValidateToken(signer);
		except:
			token = '';
			#  no token given, safe to ignore
		
		url = self.__validateUrl_(url);
		#  Fetch the content and convert it into JSON.
		#  TODO: Fetcher needs to handle variety of HTTP methods.
		result = self.__fetchContentDivert_(url, method, signer);
		#if (! isset(result)):
		if (result == None):
			# OAuthFetcher only
			metadata = self.__oauthFetcher_.getResponseMetadata();
			json = {url : metadata};
			from lib import Util;
			json = Util.json_encode(json);
			output = UNPARSEABLE_CRUFT + json;
			self.__setCachingheaders_();
			Util.header(self.response, "Content-Type: application/json; charset=utf-8");
			self.response.out.write(output);
			Util.die();
		

		status = int(result.getHttpCode());
		# Util.header("HTTP/1.1 status", True);
		if (status == 200):
			output = '';
			if (self.request.get('contentType') != None and self.request.get('contentType') == 'FEED'):
				#require 'external/Zend/Feed.php';
				numEntries = self.request.get('numEntries');
				getSummaries = self.request.get('getSummaries');
				channel = {};
				from src.common import RemoteContentRequest;
				request = RemoteContentRequest(url);
				request = self.__context_.getHttpFetcher().fetch(request, self.__context_);
				if (int(result.getHttpCode()) == 200):
					content = result.getResponseContent();	

					try:												
						from lib import Feed;
						from lib import FeedAtom;
						from lib import FeedRss;

						feed = Feed.importString(content);
						if isinstance(feed, FeedAtom):
							title = feed.title()
							link = feed.link()
							link = [{}, {}]
							id = feed.id()
							subtitle = feed.subtitle()
							entries = feed.entry()
							channel = {'title' : title, 'link': link, 'id' : id, 
								   'subtitle' : subtitle, 'items' : []};
							counter = 0;
							for entry in entries :
								if (counter >= 4):
									break;
								title = entry.title()
								id = entry.id()
								content = entry.content()
								summary = entry.summary()
								author = entry.author()
								published = entry.published()
								updated = entry.updated()
								link = entry.link()
								link = {};
								channel['items'].append({'id' : id, 'title' : title, 'link' : link, 'summary' : summary, 'content' : content, 'author' : author, 'published' : published, 'updated' : updated});
								counter = counter + 1;
						elif isinstance(feed, FeedRss):										
							title = feed.title()
							link = feed.link()
							description = feed.description()
							pubDate = feed.pubDate()
							language = feed.language()
							category = feed.category()
							items = feed.items()
							channel = {'title' : title, 'link': link, 'description' : description, 
								   'pubDate' : pubDate, 'language' : language, 'category' : category, 'items' : []};

							counter = 0;
							for item in items :
								if (counter >= numEntries):
									break;
								title = item.title()
								link = item.link()
								description = item.description()
								pubDate = item.pubDate()
								language = item.language()
								category = item.category()
								comments = item.comments()
								channel['items'].append({'description' : description, 'title' : title, 
											'link' : link, 'pubDate' : pubDate, 
											'language' : language, 'category' : category, 
											'comments' : comments});
								counter = counter + 1;

						else:
							raise Exception, 'Invalid feed type';
						
						from lib import Util;
						resp = Util.json_encode(channel);
					except Exception, e:#Zend_Feed_Exception(e):
						#raise Exception e;
						resp = "Error parsing feed:";
					
				else:
					#  feed import failed
					resp = "Error fetching feed, response code: " + result.getHttpCode();
				
			else:
				resp = result.getResponseContent();

			json = {url : {'body' : resp, 'rc' : status}};

			from lib import Util;
			json = Util.json_encode(json);
			output = UNPARSEABLE_CRUFT + json;
			
			self.__setCachingheaders_();
			Util.header(self.response, "Content-Type: application/json; charset=utf-8");
			self.response.out.write(output);
		else:
			#[todo]@ob_end_clean();
			Util.header(self.response, "HTTP/1.0 404 Not Found");
			self.response.out.write( "<html><body><h1>404 - Not Found</h1></body></html>");
		
		Util.die();
	



	 # 
	 # Fetches the content and returns it as-is using the headers as returned
	 # by the remote host.
	 # 
	 # @param string url the url to retrieve
	 # @param GadgetSigner signer the GadgetSigner to use
	 # @param string method either get or post
	 # 
	def fetch(self, url, signer, method):
		url = self.__validateUrl_(url);
		# TODO: Fetcher needs to handle variety of HTTP methods.
		result = self.__fetchContent_(url, method);

		#  TODO: Fetcher needs to handle variety of HTTP methods.

		status = int(result.getHttpCode());
		if (status == 200):
			headers = result.getResponseHeaders();
			#  Send the file as attachment

			#headers['Content-Disposition'] =  'attachment; filename=p.txt';
			arr = ["transfer-encoding", "cache-control", 'expires', 'content-length', "etag"];
			from lib import Util;
			
			for key in headers.keys():					
				if (key.lower() not in arr):						
					Util.header(self.response, key, headers[key]);
		
			#import sys;sys.exit();

			import md5;
			etag = md5.new(result.getResponseContent()).hexdigest();

			from lib import RequestUtil;
			requestUtilHandler = RequestUtil.getInstance();		

			
			import time;
			
			lastModified = result.getResponseHeader('last-modified') if result.getResponseHeader('last-modified') != None else time.strftime('%a, %d %m %Y %H:%M:%S %Z', time.localtime(result.getCreated()));
			lastModified = result.getResponseHeader('last-modified');

			notModified = False;

			#  If HTTP_PRAGMA | HTTP_CACHE_CONTROL == no-cache, the browser wants to do a 'forced reload' 
			
			if (not requestUtilHandler.SERVER.has_key('HTTP_PRAGMA') or requestUtilHandler.SERVER['HTTP_PRAGMA'].lower().find('no-cache') == -1 and (not requestUtilHandler.SERVER.has_key('HTTP_CACHE_CONTROL') or requestUtilHandler.SERVER['HTTP_CACHE_CONTROL'].lower().find('no-cache') == -1 )):
				if (requestUtilHandler.SERVER.has_key('HTTP_IF_NONE_MATCH') and requestUtilHandler.SERVER['HTTP_IF_NONE_MATCH'] == etag):
					#  if e-tag's match, set not modified, and no need to check the if-modified-since headers
					notModified = True;
				elif (requestUtilHandler.SERVER.has_key('HTTP_IF_MODIFIED_SINCE') and lastModified and not requestUtilHandler.SERVER.has_key('HTTP_IF_NONE_MATCH')):
				
					import time;

#					if_modified_since = time.mktime(time.strptime(requestUtilHandler.SERVER['HTTP_IF_MODIFIED_SINCE'], '%a, %d %b %Y %H:%M:%S %Z'));

					#  Use the request's Last-Modified, otherwise fall back on our internal time keeping (the time the request was created)
					lastModified = time.mktime(time.strptime(lastModified, '%a, %d %b %Y %H:%M:%S %Z'));
#					if (lastModified <= if_modified_since):
#						notModified = True;
					notModified = False;
					
				
			
			
#			self.__setCachingheaders_(etag, self.__context_.getRefreshInterval(), lastModified);
			from lib import Util;
			#  If the cached file time is within the refreshInterval params value and the ETag match, return not-modified
			if (notModified):
				Util.header(self.response, 'HTTP/1.0 304 Not Modified', True);
				Util.header(self.response, 'Content-Length: 0', True);
			else:
				#  then Util.write( the content
				self.response.out.write(result.getResponseContent());
			
		else:
			#@ob_end_clean();
			Util.header(self.response, "HTTP/1.0 404 Not Found", True);
			self.response.out.write("<html><body><h1>404 - Not Found (status)</h1>");
			self.response.out.write("</body></html>");
		
		#  make sure the HttpServlet destructor doesn't override ours
#		self.response.clear();
#		Util.die();	

	# 
	 # Both fetch and fetchJson call this def to retrieve the actual content
	 # 
	 # @param string url the url to fetch
	 # @param string method either get or post
	 # @return the filled in request (RemoteContentRequest)
	 # 
	def __fetchContent_(self, url, method):
		# TODO get actual character encoding from the request		

		#  Extract the request Util.headers from the RequestUtil.headers super-global (this -does- unfortunatly mean that any Util.header that php doesn't understand won't be proxied thru though)
		#  if this turns out to be a problem we could add support for HTTP_RAW_Util.headerS, but this depends on a php.ini setting, so i'd rather prevent that from being required
		headers = '';
		context = GadgetContext(self.request, self.response,'GADGET');
		requestheaders = self.__request_headers_();
		for key in requestheaders.keys():
			val = requestheaders[key];
			if (key != 'Keep-alive' and key != 'Connection' and key != 'Host' and key != 'Accept' and key != 'Accept-Encoding'):
				#  propper curl header format according to http://www.php.net/manual/en/def.curl-setopt.php#80099
				headers = headers  + key + ": " + val + "\n";

		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();		

		if (method == 'POST'):
			data = requestUtilHandler.GETORPOST['postData'] if requestUtilHandler.GETORPOST.has_key('postData') else False;
			#if (not data):
			#	data = isset(requestUtilHandler.GETORPOST['postData']) ? requestUtilHandler.GETORPOST['postData'] : False;
			
			postData = '';
			if (data):
				data = urllib.unquote(data);
				entries = data.split('&');
				for entry in entries:
					parts = entry.split('=');
					#  Process only if its a valid value=something pair
					if (len(parts) == 2):
						postData = postData + urllib.quote(parts[0]) + '=' + urllib.quote(parts[1]) + '&';
					
				
				#  chop of the trailing &
				if (len(postData)):
					postData = Util.substr(postData, 0, len(postData) - 1);
				
			
			#  even if postData is an empty string, it will still post (since RemoteContentRquest checks if its False)
			#  so the request to POST is still honored

			from src.common import RemoteContentRequest;
			request = RemoteContentRequest(url, headers, postData);
			request = self.__context_.getHttpFetcher().fetch(request, context);
		else:
			from src.common import RemoteContentRequest;
			request = RemoteContentRequest(url, headers);
			request = self.__context_.getHttpFetcher().fetch(request, context);
		
		return request;
	

	def __fetchContentDivert_(self, url, method, signer):
		
		#authz = isset(RequestUtil.GETORPOST['authz']) ? RequestUtil.GETORPOST['authz'] : (isset(RequestUtil.GETORPOST['authz']) ? RequestUtil.GETORPOST['authz'] : '');
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();		

		if (self.request.get('authz')):
			authz = self.request.get('authz');
		else:
			authz = '';

		token = self.__context_.extractAndValidateToken(signer);
		if (authz.upper() == 'SIGNED'):
			fetcher = self.__signingFetcher_.getSigningFetcher(BasicRemoteContentFetcher(), token);
			return fetcher.fetch(url, method);
		elif (authz.upper() == 'OAUTH'):
			params = OAuthRequestParams();
			fetcher = self.__signingFetcher_.getSigningFetcher(BasicRemoteContentFetcher(), token);
			oAuthFetcherFactory = OAuthFetcherFactory(fetcher);
			self.__oauthFetcher_ = oAuthFetcherFactory.getOAuthFetcher(fetcher, token, params);
			request = RemoteContentRequest(url);
			request.createRemoteContentRequestWithUri(url);
			return self.__oauthFetcher_.fetch(request);
		else:
			return self.__fetchContent_(url, method);


	def setContentFetcher(self, contentFetcherFactory):
		self.contentFetcherFactory = contentFetcherFactory;
	

	# 
	 # Sets the caching headers (overwriting anything the remote host set) to force
	 # the browser not to cache this. 
	 # 
	 # 
	def __setCachingheaders_(self, etag = False, maxAge = False, lastModified = False):
		from lib import Util;
		#[:TODO]
		import time;
		expires = (float(time.time()) + float(maxAge)) if (maxAge != False) else (time.time() - 3000);
		public = 'public' if maxAge else 'private';
		maxAge = '0' if maxAge == False else maxAge;

		cacheTime = time.strftime('%a, %d %m %Y %H:%M:%S', time.gmtime(expires));		
		
		if (etag):
			Util.header(self.response, "ETag: " + etag);
		
		if (lastModified):
			Util.header(self.response, "Last-Modified: " + lastModified);
		
		Util.header(self.response, "Cache-Control: " + public + "; max-age=" + maxAge);
		Util.header(self.response, "Expires: " + cacheTime + " GMT");	

	# 
	 # Empty def, should make something practical here some day.
	 # it's def should be to validate the given url if its in
	 # correct http(s):port:# location/url format
	 # 
	 # @param string url
	 # @return string the 'validated' url
	 # 
	def __validateUrl_(self, url):
		# TODO should really make a PHP version of the URI class and validate in all the locations the java version does
		#  why not use Zend.Uri:
		return url;
	

	def __request_headers_(self):
		# [:TODO] need to implement
		#  Try to use apache's request headers if available
		#from google.appengine.ext.webapp import Request;
		#if (Request.headers):
		#	return Request.headers;
		
		#  if that failed, try to create them from the _SERVER superglobal
		headers = {};
		from lib import Util;
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();		

		
		
		for skey in requestUtilHandler.SERVER.keys():
			if (Util.substr(skey, 0, 5) == "HTTP_"):
				#headername = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", Util.substr(skey, 0, 5)))));
				headername = Util.ucwords(skey.replace("_", " ").lower()).replace(" ", "-");
				headers[headername] = requestUtilHandler.SERVER[skey];
			
		
		return headers;
	

